WICONF.initScrollbar = {
    step: 150// 每次滚动时的位移
};
/**
 * @widoc $.fn.initScrollbar
 * @namespace aux
 * @des 在元素中创建自定义滚动条。注意：元素初始化前，应只包含一个内容元素，用于计算高度。因此若有多块内容，也请包裹在唯一的子元素中
 * 组件配置，在使用组件前调用：
 <pre class="des-conf"> $.setWiConf('initScrollbar',{
    step:150// 每次滚动时的位移
 });</pre>
 * @type function
 * @param {object} opts 配置参数
 * @param {number=} opts.step 每次滚动时的位移，单位：px，默认为 150。
 * @param {function=} opts.onScroll 组件内引起滚动时的回调。
 *   - function()
 * @return {object|undefined} obj 接口对象
 * @rtn {function} obj.fResetSize() 根据实际尺寸调整滚动条
 * @rtn {function} obj.fScrollUp(bTriggerCb) 向上滚动
 *   - bTriggerCb {boolean=} 滚动完成后是否触发回调，默认不触发
 * @rtn {function} obj.fScrollDown(bTriggerCb) 向下滚动
 *   - bTriggerCb {boolean=} 滚动完成后是否触发回调，默认不触发
 * @rtn {function} obj.fScrollTo(newTop,bTriggerCb) 滚动到指定位置
 *   - newTop {number} 要滚动到的新位置
 *   - bTriggerCb {boolean=} 滚动完成后是否触发回调，默认不触发
 * @rtn {function} obj.fScrollElIntoView(jqSub,bTriggerCb) 将 jqSub 移入滚动元素视图区
 *   - jqSub {object} 要移入滚动元素视图区的 jquery 元素
 *   - bTriggerCb {boolean=} 滚动完成后是否触发回调，默认不触发
 */
$.fn.initScrollbar = function(opts){
    var el = $(this),
        CONF = WICONF.initScrollbar,
        dom = el[0];
    opts = $.extend({
        step: CONF.step,// 每次滚动的位移
        onScroll: $.noop// 组件内引起滚动时的回调
    }, opts);
    var api,
        domCont,// 内容元素 dom 对象
        jqBar,// 滚动条 jquery 元素对象
        jqSlider;// 滚动条滑块 jquery 元素对象
    var nHeight,// 容器高度，每次 fResetSize 时获取
        nContHeight;// 内容高度，每次 fResetSize 时获取
    // 滚动操作：newTop 要滚动到的新位置，bTriggerCb=true 触发回调
    // 由于使用 el.scrollTop(newTop) 实现滚动，因此 newTop 超出合理范围时浏览器会自动调节，即 newTop 不一定是滚动后的真正位置
    var fScroll = function(newTop, bTriggerCb){
        el.scrollTop(newTop);
        fResetPos();
        if(bTriggerCb){
            opts.onScroll();// 触发回调
        }
    };
    // 向上滚动
    var fScrollUp = function(bTriggerCb){
        var oldTop = el.scrollTop();// 滚动前的位置
        if(oldTop > 0){// 未滚动到顶
            fScroll(oldTop - opts.step, bTriggerCb);// 已经判断过位置，不需要使用 fScrollTo()
        }
    };
    // 向下滚动
    var fScrollDown = function(bTriggerCb){
        var oldTop = el.scrollTop();// 滚动前的位置
        if(oldTop < nContHeight - nHeight){// 未滚动到底
            fScroll(oldTop + opts.step, bTriggerCb);// 已经判断过位置，不需要使用 fScrollTo()
        }
    };
    // 滚动到指定位置（与 fScroll 的不同在于，此方法会先验证 newTop 的合理性，若超出范围将不会引起滚动）
    var fScrollTo = function(newTop, bTriggerCb){
        var oldTop = el.scrollTop();// 滚动前的位置
        newTop = Math.max(0, newTop);// 不能小于最小位置（顶部）
        newTop = Math.min(nContHeight - nHeight, newTop);// 不能大于最大位置（底部）
        if(oldTop != newTop){
            fScroll(newTop, bTriggerCb);
        }
    };
    // 将指定元素滚动到可见范围
    var fScrollElIntoView = function(jqSub, bTriggerCb){
        var oldTop = el.scrollTop();// 滚动前的位置
        var elBCRTop = domCont.getBoundingClientRect().top,
            subBCR = jqSub.get(0).getBoundingClientRect();
        var newTop,
            minTop = (subBCR.bottom - elBCRTop) - nHeight,// jqSub 底部相对于内容的位置 - 容器高度
            maxTop = (subBCR.top - elBCRTop);// jqSub 顶部相对于内容的位置
        if(minTop > maxTop){// jqSub 高度超过容器
            newTop = maxTop;// 借 newTop 交换 minTop, maxTop
            maxTop = minTop;
            minTop = newTop;
        }
        newTop = oldTop;
        // 判断滚动前的位置
        if(oldTop < minTop){
            newTop = minTop;
        }
        else if(oldTop > maxTop){
            newTop = maxTop;
        }
        else{
            return;
        }
        fScroll(newTop, bTriggerCb);
    };
    // 绑定滚动监听
    var fBindScrollEvent = function(){
        if(navigator.userAgent.indexOf("Firefox") > 0){// 火狐
            dom.addEventListener('DOMMouseScroll', function(e){
                //fScroll(e.detail > 0, true);
                if(e.detail > 0){
                    fScrollDown(true);
                }
                else{
                    fScrollUp(true);
                }
                if(nHeight < nContHeight){// 有滚动条时，阻止默认事件
                    e.preventDefault();
                }
            }, false);
            el.scrollTop(0);// 避免初始化进入页面时重复载入
        }
        else{// 其他浏览器
            el.on('mousewheel', function(e){
                //fScroll(e.originalEvent.wheelDelta < 0, true);
                if(e.originalEvent.wheelDelta < 0){
                    fScrollDown(true);
                }
                else{
                    fScrollUp(true);
                }
                if(nHeight < nContHeight){// 有滚动条时，阻止默认事件
                    e.preventDefault();
                }
            });
        }
    };
    // 根据实际尺寸调整滚动条位置
    var fResetPos = function(){
        var curTop = el.scrollTop();
        jqBar.css('top', curTop + 'px');
        jqSlider.css('top', curTop / nContHeight * nHeight + 'px');
    };
    // 根据实际尺寸调整滚动条大小，并调用 fResetPos 重算位置
    var fResetSize = function(){
        var curTop = el.scrollTop();
        nHeight = dom.clientHeight;// 容器尺寸
        nContHeight = domCont.clientHeight;// 内容尺寸
        var maxTop = nContHeight - nHeight;
        if(maxTop < curTop){
            el.scrollTop(maxTop);
        }
        if(nHeight < nContHeight){
            jqBar.addClass('active');
            jqSlider.css('height', (nHeight * 100 / nContHeight).toFixed(2) + '%');
            fResetPos();
        }
        else{
            jqBar.removeClass('active');
        }
    };
    var fInit = function(){
        domCont = dom.children[0];
        jqBar = $('<div class="wi-scrollbar"></div>');
        jqSlider = $('<div class="wi-scrollbar-slider"></div>');
        jqBar.append(jqSlider);
        el.css('position') == 'static' && el.css('position', 'relative');// 设置为定位元素
        el.append(jqBar);// 添加滚动条
        fResetSize();// 根据实际尺寸调整滚动条位置及大小
        fBindScrollEvent();// 绑定滚动监听
    };
    api = {
        fResetSize: fResetSize,
        fScrollUp: fScrollUp,
        fScrollDown: fScrollDown,
        fScrollTo: fScrollTo,
        fScrollElIntoView: fScrollElIntoView
    };
    fInit();
    return api;
};